`timescale 1ns / 1ps

module udp_transmit_test(
	input          		reset_i,
	input               clk_25m_i,
	input         	 	rgmii_rx_clk_i,
	input          		rgmii_rx_ctl_i,
	input [3:0]     	rgmii_rx_data_i,
	
	output wire        	rgmii_tx_clk_o,
	output wire         rgmii_tx_ctl_o,
	output wire [3:0]   rgmii_tx_data_o,
	
	output wire         phy_reset_o
);


// 192.168.1.2(c0a80102)    192.168.2.245(c0a802F5)   192.168.2.169(c0a802A9)
parameter  SRC_UDP_PORT    = 16'h22b8;					// 8888(22b8)
parameter  SRC_IP_ADDR     = 32'hc0a802F5;			
parameter  SRC_MAC_ADDR    = 48'hFABBCCDDEE33;
parameter  DST_UDP_PORT    = 16'h22b7;					// 8887(22b7)
parameter  DST_IP_ADDR     = 32'hc0a802A9;			

wire [7:0]	gmii_tx_data;
wire        gmii_tx_en;
wire [7:0]	gmii_rx_data;
wire        gmii_rx_dv;

wire       	app_rx_data_valid;
wire [7:0]	app_rx_data;
wire [15:0] app_rx_data_length;
wire [15:0] app_rx_port_num;


wire        udp_tx_ready;
wire        app_tx_ack;

reg         app_tx_data_request;
reg         app_tx_data_valid;
wire [7:0]  app_tx_data;
reg  [15:0] udp_data_length;
reg         app_tx_data_read;
wire [11:0] udp_packet_fifo_data_cnt;
reg [11:0]  fifo_read_data_cnt;
reg [1:0]   STATE;
wire 		clk_25;
wire        reset;
wire        mmcm_locked;


localparam  WAIT_UDP_DATA = 2'd0;
localparam  WAIT_ACK = 2'd1;
localparam  SEND_UDP_DATA = 2'd2;
localparam  DELAY = 2'd3;

assign  reset = ~reset_i;
assign  phy_reset_o =  1'b1;


pll clk_wiz_0
(
	.refclk   (clk_25m_i       ), 
	.clk0_out (clk_25       ), 
	.clk1_out (clk_125      ), 
	.clk2_out (clk_125_90   ), 
	.reset    (reset        ), 
	.extlock  (mmcm_locked  )
); 

udp_fifo#(
	.FIFO_PTR     (12			),     
	.FIFO_WIDTH   (8			),
	.FIFO_MODE    ("SHOWAHEAD"	)//"NORMAL" "SHOWAHEAD"
)
 udp_packet_fifo 
(
	  .rst          (reset                   ), // input rst
	  .wr_clk       (RGMII_reference_clk     ), // input wr_clk
	  .rd_clk       (RGMII_reference_clk     ), // input rd_clk
	  .din          (app_rx_data             ), // input [7 : 0] din
	  .wr_en        (app_rx_data_valid       ), // input wr_en
	  .rd_en        (app_tx_data_read        ), // input rd_en
	  .dout         (app_tx_data             ), // output [7 : 0] dout
	  .full         (                        ), // output full
	  .empty        (                        ), // output empty
	  .rd_data_count(udp_packet_fifo_data_cnt) // output [11 : 0] rd_data_count
);


always@(posedge RGMII_reference_clk or posedge reset)
	begin
		if(reset) 
			udp_data_length <= 16'd0;
		else begin 
			if(app_rx_data_valid)
		      udp_data_length <= app_rx_data_length;
			else
				udp_data_length <= udp_data_length;
		end
	end

always@(posedge RGMII_reference_clk or posedge reset)
	begin
		if(reset) begin
		   	app_tx_data_request <= 1'b0;
		   	app_tx_data_read <= 1'b0;
			app_tx_data_valid <= 1'b0;
			fifo_read_data_cnt <= 12'd0;
			STATE <= WAIT_UDP_DATA;
		end
		else begin
		   	case(STATE)
				WAIT_UDP_DATA:
					begin
						if((udp_packet_fifo_data_cnt > 12'd0) && (~app_rx_data_valid) && udp_tx_ready) begin
						   	app_tx_data_request <= 1'b1;
							STATE <= WAIT_ACK;
						end
						else begin
						   	app_tx_data_request <= 1'b0;
							STATE <= WAIT_UDP_DATA;
						end
					end
				WAIT_ACK:
					begin
					   if(app_tx_ack) begin
						   	app_tx_data_request <= 1'b0;
							app_tx_data_read <= 1'b1;
							app_tx_data_valid <= 1'b1;
							STATE <= SEND_UDP_DATA;
						end
						else begin
							app_tx_data_request <= 1'b1;
						  	app_tx_data_read <= 1'b0;
							app_tx_data_valid <= 1'b0;
							STATE <= WAIT_ACK;
						end
					end
				SEND_UDP_DATA:
					begin
						if(fifo_read_data_cnt == (udp_data_length - 1'b1)) begin
							fifo_read_data_cnt <= 12'd0;
							app_tx_data_valid <= 1'b0;
							app_tx_data_read <= 1'b0;
							STATE <= WAIT_UDP_DATA;
						end
						else begin
							fifo_read_data_cnt <= fifo_read_data_cnt + 1'b1;
							app_tx_data_valid <= 1'b1;
							app_tx_data_read <= 1'b1;
							STATE <= SEND_UDP_DATA;
						end						
					end
				DELAY:
					begin
						if(app_rx_data_valid)
							STATE <= WAIT_UDP_DATA;
						else
							STATE <= DELAY;
					end
				default: STATE <= WAIT_UDP_DATA;
			endcase
		end
	end
	
	 
udp_ip_protocol_stack #
(
	 .LOCAL_PORT_NUM     (SRC_UDP_PORT      ),
	 .LOCAL_IP_ADDRESS   (SRC_IP_ADDR       ),
	 .LOCAL_MAC_ADDRESS  (SRC_MAC_ADDR	    ),
	 .CRC_CHECK_EN       (1'b1				),
	 .CRC_GEN_EN         (1'b1				),
	 .INTER_FRAME_GAP    (4'd12				)
)
udp_ip_protocol_stack
(
    .clk					(RGMII_reference_clk), 
    .phy_tx_clk				(clk_125            ),
	.phy_rx_clk				(RGMII_reference_clk),
    .reset					(reset              ), 
    .udp_tx_ready			(udp_tx_ready       ), 
    .app_tx_ack			    (app_tx_ack         ), 
    .app_tx_request		    (app_tx_data_request), 
    .app_tx_data_valid	    (app_tx_data_valid  ), 
    .app_tx_data			(app_tx_data        ), 
    .app_tx_data_length	    (udp_data_length    ), 
    .app_tx_dst_port		(DST_UDP_PORT   ), 
    .ip_tx_dst_address	    (DST_IP_ADDR     ), 
    .app_rx_data_valid	    (app_rx_data_valid  ), 
    .app_rx_data			(app_rx_data        ), 
    .app_rx_data_length	    (app_rx_data_length ), 
    .app_rx_port_num		(app_rx_port_num    ), 
    .gmii_rx_data_valid	    (gmii_rx_dv         ), 
    .gmii_rx_data			(gmii_rx_data       ), 
    .gmii_tx_data_valid	    (gmii_tx_en         ), 
    .gmii_tx_data			(gmii_tx_data       ), 
    .ip_rx_error			(ip_rx_error        ), 
    .mac_rx_error			(mac_rx_error       )
);
	 
rgmii_send rgmii_send_module (
    .reset                 (reset             ), 
    .RGMII_reference_clk   (clk_125           ),
	.RGMII_reference_clk_90(clk_125           ),
    .mac_tx_data_valid     (gmii_tx_en        ), 
    .mac_tx_data           (gmii_tx_data      ), 
    .RGMII_tx_clk          (rgmii_tx_clk_o ), 
    .RGMII_tx_ctrl         (rgmii_tx_ctl_o ), 
    .RGMII_tx_data         (rgmii_tx_data_o)
);
	 
	 	 
rgmii_receive rgmii_receive_module (
    .reset              (reset              ), 
    .RGMII_rx_clk       (rgmii_rx_clk_i  ), 
    .RGMII_rx_ctrl      (rgmii_rx_ctl_i  ), 
    .RGMII_rx_data      (rgmii_rx_data_i ), 
    .RGMII_reference_clk(RGMII_reference_clk), 
    .GMII_data_valid    (gmii_rx_dv         ), 
    .GMII_rx_error      (                   ), 
    .GMII_rx_data       (gmii_rx_data       )
);


endmodule
